/*
*
* 版权所有 Copyleft(c) 2005
* 刘炯个人工作室 Sharp-Mark Personal Studio
*
* 允许自由使用，复制，修改，传播和贩卖此源程序及其文档，而不必支付任何费用。
* 以上的版权信息必须出现在每一份复制品中。同时这份版权信息和许可声明
* 也必须出现在支持文档中。刘炯个人工作室不对此软件适于那种具体
* 用途做出任何阐述。此代码只作为代码本身提供对其品质不做任何保证。
*
*/

#pragma once

#include <cmath>
#include <assert.h>

template<class T>
class CMatrix;

// 四维向量
template<class T>
class CVec4 {
  
public:

	CVec4(T d0 = static_cast<T>(0),
		T d1 = static_cast<T>(0),
		T d2 = static_cast<T>(0),
		T d3 = static_cast<T>(0))
	{
		_data[0] = d0;
		_data[1] = d1;
		_data[2] = d2;
		_data[3] = d3;
	}

	// 数据访问
	const T& operator[](int i) const
	{
		assert(IsIndexCurrent(i));
	    return _data[i];
	}

	T& operator[](int i)
	{
		assert(IsIndexCurrent(i));
		return _data[i];
	}

	// 坐标值
	T x() const { return _data[0]; }
	T y() const { return _data[1]; }
	T z() const { return _data[2]; }
	T w() const { return _data[3]; }

	// 将坐标值转化为颜色值
	int r() const { return static_cast<int>(_data[0]) % 255; }
	int g() const { return static_cast<int>(_data[1]) % 255; }
	int b() const { return static_cast<int>(_data[2]) % 255; }
	int a() const { return static_cast<int>(_data[3]) % 255; }

	// 向量长度
	T Length() const
	{
		T len = sqrt( static_cast<T>(_data[0] * _data[0] + _data[1] * _data[1] +
			_data[2] * _data[2] + _data[3] * _data[3]) );
	return len;
	}

	// 转换
	void Scale(T d0, T d1, T d2, T d3)	// 缩放
	{
		_data[0] *= d0;
		_data[1] *= d1;
		_data[2] *= d2;
		_data[3] *= d3;
	}
	void Normalize()					// 转化为单位向量
	{
		T l = Length();
		if (l > 0) {
			_data[0] /= l;
			_data[1] /= l;
			_data[2] /= l;
		}
	}
	void Transform(const CMatrix<T>& mat) {
		CVec4 answer;
		for (int x = 0; x < 4; x++) {
			answer._data[x] = 0;
			for (int i = 0; i < 4; i++) {
				answer._data[x] += mat._data[x][i] * _data[i];
			}
		}
		(*this) = answer;
	}
	void Translate(T x, T y, T z)
	{
		CMatrix<T> t;
		t.SetIdentity();
		t._data[0][3] = x;
		t._data[1][3] = y;
		t._data[2][3] = z;

		CVec4 temp = *this;
		temp = t * temp;
		*this = temp;

		*this = t * (*this);		// 不可写做(*this) * t，顺序在矩阵乘法中很重要
	}

	void XRotate(float angel)
	{
		CMatrix<T> rx;
		rx.SetIdentity();
		rx._data[1][1] = static_cast<T>(std::cos(angel));
		rx._data[1][2] = static_cast<T>(-std::sin(angel));
		rx._data[2][1] = static_cast<T>(std::sin(angel));
		rx._data[2][2] = static_cast<T>(std::cos(angel));
		*this = rx * (*this);
	}

	void YRotate(float angel)
	{
		CMatrix<T> ry;
		ry.SetIdentity();
		ry._data[0][0]= static_cast<T>(std::cos(angel));
		ry._data[0][2]= static_cast<T>(std::sin(angel));
		ry._data[2][0]= static_cast<T>(-std::sin(angel));
		ry._data[2][2]= static_cast<T>(std::cos(angel));
		*this = ry * (*this);
	}

	void ZRotate(float angel)
	{
		CMatrix<T> rz;
		rz.SetIdentity();
		rz._data[0][0]= static_cast<T>(std::cos(angel));
		rz._data[0][1]= static_cast<T>(-std::sin(angel));
		rz._data[1][0]= static_cast<T>(std::sin(angel));
		rz._data[1][1]= static_cast<T>(std::cos(angel));
		*this = rz * (*this);
	}

	// 操作符重载
	CVec4& operator=(const CVec4 &rhs)
	{
		_data[0] = rhs._data[0];
		_data[1] = rhs._data[1];
		_data[2] = rhs._data[2];
		_data[3] = rhs._data[3];
		return *this;
	}

	bool operator==(const CVec4 &rhs) const
	{
		return ((_data[0] == rhs._data[0]) &&
            (_data[1] == rhs._data[1]) &&
			(_data[2] == rhs._data[2]) &&
			(_data[3] == rhs._data[3]));
	}

	bool operator!=(const CVec4& rhs) const
	{
		return !((*this) == rhs);
	}

	CVec4& operator+=(const CVec4& rhs)
	{
		_data[0] += rhs._data[0];
		_data[1] += rhs._data[1];
		_data[2] += rhs._data[2];
		_data[3] += rhs._data[3];
		return *this;
	}

	CVec4& operator-=(const CVec4& rhs)
	{
		_data[0] -= rhs._data[0];
		_data[1] -= rhs._data[1];
		_data[2] -= rhs._data[2];
		_data[3] -= rhs._data[3];
		return *this;
	}

	CVec4& operator*=(const T& rhs)
	{
		_data[0] *= rhs;
		_data[1] *= rhs;
		_data[2] *= rhs;
		_data[3] *= rhs;
		return *this;
	}

	CVec4& operator/=(const T& rhs)
	{
		_data[0] /= rhs;
		_data[1] /= rhs;
		_data[2] /= rhs;
		_data[3] /= rhs;
		return *this;
	}

	const CVec4 operator+(const CVec4 &rhs) const
	{
		CVec4 result;
		result._data[0] = _data[0] + rhs._data[0];
		result._data[1] = _data[1] + rhs._data[1];
		result._data[2] = _data[2] + rhs._data[2];
		result._data[3] = _data[3] + rhs._data[3];
		return result;
	}

	const CVec4 operator-(const CVec4 &rhs) const
	{
		CVec4 result;
		result._data[0] = _data[0] - rhs._data[0];
		result._data[1] = _data[1] - rhs._data[1];
		result._data[2] = _data[2] - rhs._data[2];
		result._data[3] = _data[3] - rhs._data[3];
		return result;
	}

	const CVec4 operator*(const T& rhs) const
	{
		CVec4 result;
		result._data[0] = _data[0] * rhs;
		result._data[1] = _data[1] * rhs;
		result._data[2] = _data[2] * rhs;
		result._data[3] = _data[3] * rhs;
		return result;
	}

	const CVec4 operator/(const T& rhs) const
	{
		CVec4 result;
		result._data[0] = _data[0] / rhs;
		result._data[1] = _data[1] / rhs;
		result._data[2] = _data[2] / rhs;
		result._data[3] = _data[3] / rhs;
		return *this;
	}

	void DivideByW() {
		if (_data[3] != 0)
		{
			_data[0] /= _data[3];
			_data[1] /= _data[3];
			_data[2] /= _data[3];
		} 
		else
		{
			_data[0] = _data[1] = _data[2] = 0;
		}
		_data[3] = 1;
	}
	friend const CVec4 operator/(T& lhs, const CVec4& rhs)
	{
		return rhs / lhs;
	}

	friend const CVec4 operator*(T& lhs, const CVec4& rhs)
	{
		return rhs * lhs;
	}

private:

	friend class CMatrix<T>;

	bool IsIndexCurrent(int i) const
	{
		if (i < 0 || i >= 4)
			return false;
		else
			return true;
	}

	T _data[4];
};
